/* 
 *  Copyright (c) 2020-2021 Xuhpclab. All rights reserved.
 *  Licensed under the MIT License.
 *  See LICENSE file for more information.
 */

#include <map>
#include <string>
#include <vector>
#include <algorithm>
#include <sys/stat.h>
#include <math.h>

#include "dr_api.h"
#include "drmgr.h"
#include "drcctlib.h"
#include "drcctlib_hpcviewer_format.h"

using namespace std;

#define DRCCTLIB_PRINTF(_FORMAT, _ARGS...) \
    DRCCTLIB_PRINTF_TEMPLATE("cache_false_sharing_detection_hpc_fmt", _FORMAT, ##_ARGS)
#define DRCCTLIB_EXIT_PROCESS(_FORMAT, _ARGS...) \
    DRCCTLIB_CLIENT_EXIT_PROCESS_TEMPLATE("cache_false_sharing_detection_hpc_fmt", _FORMAT, ##_ARGS)

#define MAX_CLIENT_CCT_PRINT_DEPTH 10

#define SAMPLE_RUN
#ifdef SAMPLE_RUN
#    define UNITE_NUM 1000000
#    define SAMPLE_NUM 100000
#endif

static std::string g_folder_name;
static int tls_idx;

typedef struct _cache_contention_t {
    int miss_count;
    int inval_count;
} cache_contention_t;

typedef struct _per_thread_t {
    map<uint64_t, int> *false_sharing_map;
    int pt_id;
    file_t output_file;
#ifdef SAMPLE_RUN
    uint32_t cur_mem_idx;
#endif
// #define DEBUG_CACHE_CONTENTION
#ifdef DEBUG_CACHE_CONTENTION
    file_t log_file;
#endif
} per_thread_t;

//��ʼ��һ��ȫ���ļ�����������̵߳��ܼ���Ϣ
file_t global_output_file;
//��ʼ��һ��ȫ���ļ���������Ϻ�������߳���Ϣ
file_t global_sum_file;

//ȫ��map�����ڹ��������̵߳���Ϣ��
map<uint64_t, int> *global_false_sharing_map = new map<uint64_t, int>();

//����create��ͬ��Ԫ�غϲ�����ͬһ����������ͬ�߳�ʹ�õ������������
//keyֵ��ͬ��Ԫ�ؽ��кϲ���value���
map<int, int> *global_sum_up_false_sharing_map = new map<int, int>();

map<int, cache_contention_t> *thread_count_map = new map<int, cache_contention_t>();
static long long int total_miss_count = 0;
static long long int total_invalidation_count = 0;
static long long int total_cold_miss_count = 0;
static long long int total_mem_ref_count = 0;
static long long int total_false_sharing_count = 0;

//定义所要模拟的cache line大小，单位是字节（byte�?
#define CacheLineSize   64

//定义将访存地址转换为cache line对齐�?
//（是否可以考虑修改drcctlib.cpp中的fwt_init_shadow_memory函数？只需要每个开始地址存储一个unit即可，因为保证访存地址进行cache line align�?
//目前是以cachelinesize=64计算的align，最�?位清�?
#define CacheLineAlign(_addr)   _addr&0xffffffffffffffc0
//一个字（word）大小为4bytes�?2位，最�?位清�?
#define WordAlign(_addr)    _addr&0xfffffffffffffffc

//return true means happens cache miss
//return false means goes on, nothing happens
inline bool
test_miss(cache_cont_t cur_bitmap, int pt_id)
{
    return !((cur_bitmap.bitmap & (1 << pt_id)) > 0);
}

//return true means happens cache invalidation
//return false means goes on, nothing happens
inline bool
test_inval(cache_cont_t cur_bitmap, int pt_id)
{
    return (cur_bitmap.bitmap & (~(1 << pt_id))) > 0;
}

//use OR instruction to add a thread into bitmap
inline void
after_miss_happens_refresh_bitmap(cache_cont_t* cur_bitmap, int pt_id)
{
    (*cur_bitmap).bitmap = (*cur_bitmap).bitmap | (1 << pt_id);
    return ;
}

//use MOV instruction to clear all other thread flag, and set its exclusive own flag
inline void
after_invalidation_happens_refresh_bitmap(cache_cont_t* cur_bitmap, int pt_id, int start_word_num, int end_word_num)
{
    (*cur_bitmap).bitmap = 1 << pt_id;
    for (int i = 0; i < 16; i++){
        (*cur_bitmap).word_read_bitmap[i] = 0;
        (*cur_bitmap).word_write_bitmap[i] = 0;
    }
    for (int i = start_word_num; i < end_word_num + 1; i++) {
        (*cur_bitmap).word_write_bitmap[i] = 1 << pt_id;
    }
    return ;
}

inline void
update_miss_count(int pt_id)
{
    map<int, cache_contention_t>::iterator it = (*thread_count_map).find(pt_id);
    if (it != (*thread_count_map).end()) {
        it->second.miss_count++;
    } else {
        cache_contention_t new_cont;
        new_cont.miss_count = 1;
        new_cont.inval_count = 0;
        (*thread_count_map).insert(pair<int, cache_contention_t>(pt_id, new_cont));
    }
    total_miss_count++;
    return ;
}

inline void
update_inval_count(int pt_id) 
{
    map<int, cache_contention_t>::iterator it = (*thread_count_map).find(pt_id);
    if (it != (*thread_count_map).end()) {
        it->second.inval_count++;
    } else {
        cache_contention_t new_cont;
        new_cont.miss_count = 0;
        new_cont.inval_count = 1;
        (*thread_count_map).insert(pair<int, cache_contention_t>(pt_id, new_cont));
    }
    total_invalidation_count++;
    return ;
}

inline void
update_read_bitmap(cache_cont_t *bitmap, int start_word, int end_word, int pt_id)
{
    for (int i = start_word; i < end_word + 1; i++) {
        (*bitmap).word_read_bitmap[i] = (*bitmap).word_read_bitmap[i] | (1 << pt_id);
    }
}

inline void
update_write_bitmap(cache_cont_t *bitmap, int start_word, int end_word, int pt_id)
{
    for (int i = start_word; i < end_word + 1; i++) {
        (*bitmap).word_write_bitmap[i] = (*bitmap).word_write_bitmap[i] | (1 << pt_id);
    }
}

inline bool
test_false_sharing_when_miss(cache_cont_t bitmap, int start_word, int end_word, int pt_id, bool *happen)
{
    for (int i = start_word; i < end_word + 1; i++) {
        (*happen) = (*happen) || (((bitmap.word_write_bitmap[i]) & (~(1 << pt_id))) > 0);
    }
    return *happen;
}

inline bool
test_false_sharing_when_inval(cache_cont_t bitmap, int start_word, int end_word, int pt_id, bool *happen)
{
    for (int i = start_word; i < end_word + 1; i++) {
        (*happen) = (*happen) || ((((bitmap.word_write_bitmap[i]) & (~(1 << pt_id))) > 0) ||
                                    (((bitmap.word_read_bitmap[i]) & (~(1 << pt_id))) > 0));
    }
    return *happen;
}

inline void
store_false_sharing_info(void* drcontext, context_handle_t cur_ctxt_hndl, uint64_t addr, per_thread_t *pt)
{
    map<uint64_t, int> *temp_map = pt->false_sharing_map;
    data_handle_t data_hndl = 
                drcctlib_get_data_hndl_ignore_stack_data(drcontext, (void*)addr);
    context_handle_t create_hndl = 0;
    if (data_hndl.object_type == DYNAMIC_OBJECT) {
        create_hndl = data_hndl.path_handle;
    } else if (data_hndl.object_type == STATIC_OBJECT) {
        create_hndl = -data_hndl.sym_name;
    }

    uint64_t new_pair = (((uint64_t)cur_ctxt_hndl) << 32) + create_hndl;
    map<uint64_t, int>::iterator it = (*temp_map).find(new_pair);
    if (it != (*temp_map).end()) {
        it->second++;
    } else {
        int i = 1;
        (*temp_map).insert(pair<uint64_t, int>(new_pair, i));
    }
}


// client want to do
inline void
DoWhatClientWantTodo(void *drcontext, per_thread_t *pt, context_handle_t cur_ctxt_hndl,
                     app_pc cur_addr, char type, ushort size)
{
    //获取当前运行线程的id
   int cur_thread_id = drcctlib_get_thread_id();
   //地址对齐
   uint64_t aligned_addr = CacheLineAlign((uint64_t) cur_addr);

   uint64_t start_addr = (uint64_t)cur_addr;
   uint64_t end_addr = start_addr + size;
   //开始的第几个字（从0开始计数）
   int start_word = WordAlign((start_addr - aligned_addr)) / 4;
   int end_word;
   cache_cont_t* cur_bitmap;

   //检测访存空间的大小，若访存数据大小超过一个Cache Line的大小，则需要分多个Cache Line处理
   //Cache_Line_nums代表该次访存涉及几个Cache Line
   int Cache_Line_nums = 0;
   if (((uint64_t)cur_addr + size) <= (aligned_addr + 64)) {
       Cache_Line_nums = 1;
   } else {
       int size_remain = size - (aligned_addr + 64 - (uint64_t)cur_addr);
       Cache_Line_nums = 1 + (int)ceil((double)size_remain / (double)CacheLineSize);
   }
   
   for (int i = 0; i < Cache_Line_nums; i++) {
       int start_word_num = 0;
       int end_word_num = 0;
       //下面判断处理Cache Line中第几个字的过程
       if (i == 0) {
           //第一次进入，是最初始的Cache Line，开头部�?
           start_word_num = start_word;
           if (Cache_Line_nums == 1) {
               end_word_num = WordAlign((end_addr - aligned_addr)) / 4;
           } else {
               end_word_num = 15;
           }
       } else if (i == Cache_Line_nums - 1) {
           //结尾部分
           start_word_num = 0;
           end_word_num = WordAlign((end_addr - aligned_addr)) / 4;
       } else {
           //中间部分
           start_word_num = 0;
           end_word_num = 15;
       }
        //初次访问探测机制
        if(!fwt_test_shadow_memory((size_t)aligned_addr)) {
            //未初始化过，需进行init
            //total_cold_miss_count++;
            fwt_init_shadow_memory((void*)aligned_addr, CacheLineSize);
	        cur_bitmap = fwt_get_shadow_memory((void*) aligned_addr);
	        after_miss_happens_refresh_bitmap(cur_bitmap, cur_thread_id);
         //dr_printf("cold miss *** addr is : %x, cur_thread_id is : %d \n", aligned_addr, cur_thread_id);
	    continue;
        }

        //对于已经有内容的shadow memory地址，将其存储的信息取出
        cur_bitmap = fwt_get_shadow_memory((void*) aligned_addr);

        if(type == 0) {
            //说明该内存访问操作是读操作，检测cache miss
            //对于读操作，更新相关字（word）的read bitmap
            update_read_bitmap(cur_bitmap, start_word_num, end_word_num, cur_thread_id);
            if (test_miss(*cur_bitmap, cur_thread_id)) {
                bool *happen_false_sharing = new bool;
                (*happen_false_sharing) = false;
                if (test_false_sharing_when_miss(*cur_bitmap, start_word_num, 
                                                    end_word_num, cur_thread_id, happen_false_sharing)) 
                {
                    if (i == 0) { //最开始应使用start addr，因为访问的起始地址不一定是Cache Line对齐�?
                        store_false_sharing_info(drcontext, cur_ctxt_hndl, start_addr, pt);
                    } else { //若访问的数据跨Cache Line，那么除了起始地址，后续使用data hndl查找date object时，都可以使用aligned addr
                        store_false_sharing_info(drcontext, cur_ctxt_hndl, aligned_addr, pt);
                    }
                }
            //dr_printf("after test miss *** addr is : %x, cur_thread_id is : %d \n", aligned_addr, cur_thread_id);
                //update_miss_count(cur_thread_id);
                after_miss_happens_refresh_bitmap(cur_bitmap, cur_thread_id);
            }
        } else {
            //对于写操作，更新相关字（word）的write bitmap
            update_write_bitmap(cur_bitmap, start_word_num, end_word_num, cur_thread_id);
		//dr_printf("write addr is : %x, cur_thread_id is %d \n", aligned_addr, cur_thread_id);
            //说明该内存访问操作是写操作，检测cache invalidation
            if (test_inval(*cur_bitmap, cur_thread_id)) {
                bool *happen_false_sharing = new bool;
                (*happen_false_sharing) = false;
                if (test_false_sharing_when_inval(*cur_bitmap, start_word_num,
                                                    end_word_num, cur_thread_id, happen_false_sharing))
                {
                    if (i == 0) { //最开始应使用start addr，因为访问的起始地址不一定是Cache Line对齐�?
                        store_false_sharing_info(drcontext, cur_ctxt_hndl, start_addr, pt);
                    } else { //若访问的数据跨Cache Line，那么除了起始地址，后续使用data hndl查找date object时，都可以使用aligned addr
                        store_false_sharing_info(drcontext, cur_ctxt_hndl, aligned_addr, pt);
                    }
                }
		//dr_printf("happens inval! cur_bitmap is : %d, cur_thread_id is %d\n", cur_bitmap->bitmap, cur_thread_id);
                //update_inval_count(cur_thread_id);
                after_invalidation_happens_refresh_bitmap(cur_bitmap, cur_thread_id, start_word_num, end_word_num);
		//dr_printf("after refresh bitmap is : %d, cur_thread_id is %d\n", cur_bitmap->bitmap, cur_thread_id);
            }
        }

        //对于一次访存空间大于一个Cache Line Size的情况，循环判断全部地址
        //当前Cache Line判断结束，地址增加512bit，即64 bytes，即为下一个Cache Line的起始地址
        aligned_addr += 64;
   }
}

static inline void
InstrumentPerBBCache(void *drcontext, context_handle_t ctxt_hndl, int32_t slot_num,
                     int32_t mem_ref_num, mem_ref_msg_t *mem_ref_start, void **data)
{
    per_thread_t *pt;
    if (*data != NULL) {
        pt = (per_thread_t *)*data;
    } else {
        pt = (per_thread_t *)drmgr_get_tls_field(drcontext, tls_idx);
        *data = pt;
    }
    
    //total_mem_ref_count += mem_ref_num;
    for (int32_t i = 0; i < mem_ref_num; i++) {
        if (mem_ref_start[i].slot >= slot_num) {
            break;
        }

        data_handle_t data_hndl_test = drcctlib_get_data_hndl(drcontext, (void*)(uint64_t)mem_ref_start[i].addr);
        if(data_hndl_test.object_type == UNKNOWN_OBJECT)
            continue;
        
        //dr_printf("not unknown\n");
        //DoWhatClientWantTodo(drcontext, pt, ctxt_hndl + mem_ref_start[i].slot,
                             //mem_ref_start[i].addr, mem_ref_start[i].type, mem_ref_start[i].size);
        #ifdef SAMPLE_RUN
        pt->cur_mem_idx++;
        if (pt->cur_mem_idx > 0 && (pt->cur_mem_idx % UNITE_NUM <= SAMPLE_NUM) && read_main_flag()) {
        //dr_printf("id is %d : cur_mem_idx is %d\n", drcctlib_get_thread_id(), pt->cur_mem_idx);
          //dr_printf("in do what client want to do\n");
          DoWhatClientWantTodo(drcontext, pt, ctxt_hndl + mem_ref_start[i].slot,
                             mem_ref_start[i].addr, mem_ref_start[i].type, mem_ref_start[i].size);
        } else {
          //dr_printf("id is %d : cur_mem_idx is %d \n", drcctlib_get_thread_id(), pt->cur_mem_idx);
        }
        #else
        if (read_main_flag() == 0)
            continue;
        DoWhatClientWantTodo(drcontext, pt, ctxt_hndl + mem_ref_start[i].slot,
                             mem_ref_start[i].addr, mem_ref_start[i].type, mem_ref_start[i].size);
        #endif
    }
}

#ifdef DEBUG_CACHE_CONTENTION
static void
ThreadDebugFileInit(per_thread_t *pt)
{
    int32_t id = drcctlib_get_thread_id();
    char name[MAXIMUM_FILEPATH] = "";
    sprintf(name + strlen(name), "%s/thread-%d.debug.log", g_folder_name.c_str(), id);
    pt->log_file = dr_open_file(name, DR_FILE_WRITE_APPEND | DR_FILE_ALLOW_LARGE);
    DR_ASSERT(pt->log_file != INVALID_FILE);
}
#endif

static void
ThreadOutputFileInit(per_thread_t *pt)
{
    int32_t id = drcctlib_get_thread_id();
    char name[MAXIMUM_FILEPATH] = "";
    sprintf(name + strlen(name), "%s/thread-%d.topn.log", g_folder_name.c_str(), id);
    pt->output_file = dr_open_file(name, DR_FILE_WRITE_OVERWRITE | DR_FILE_ALLOW_LARGE);
    DR_ASSERT(pt->output_file != INVALID_FILE);
}

static void
ClientThreadStart(void *drcontext)
{
    per_thread_t *pt = (per_thread_t *)dr_thread_alloc(drcontext, sizeof(per_thread_t));
    if (pt == NULL) {
        DRCCTLIB_EXIT_PROCESS("pt == NULL");
    }
    drmgr_set_tls_field(drcontext, tls_idx, (void *)pt);

    pt->false_sharing_map = new map<uint64_t, int>();
    pt->pt_id = drcctlib_get_thread_id();
    #ifdef SAMPLE_RUN
    pt->cur_mem_idx=0;
    #endif
    ThreadOutputFileInit(pt);
#ifdef DEBUG_CACHE_CONTENTION
    ThreadDebugFileInit(pt);
#endif
}

inline void
print_false_sharing_info(per_thread_t *pt, uint64_t ctxt, int count)
{
    uint64_t create_cur_ctxt = ctxt;
    int create_ctxt = (int)ctxt;
    int cur_ctxt = (int)(ctxt >> 32);
    dr_fprintf(pt->output_file,
                   "=========================create=========================\n");
    if (create_ctxt > 0) {
        drcctlib_print_backtrace(pt->output_file, (context_handle_t)create_ctxt,
                                true, true, MAX_CLIENT_CCT_PRINT_DEPTH);
    } else if (create_ctxt < 0) {
        dr_fprintf(pt->output_file, "STATIC_OBJECT %s\n",
                    drcctlib_get_str_from_strpool(-create_ctxt));
    } else {
        dr_fprintf(pt->output_file, "STACK_OBJECT/UNKNOWN_OBJECT\n");
    }
    dr_fprintf(pt->output_file,
                   "===========================use===========================\n");
    drcctlib_print_backtrace(pt->output_file, (context_handle_t)cur_ctxt, true,
                            true, MAX_CLIENT_CCT_PRINT_DEPTH);
    dr_fprintf(pt->output_file,
                    "false sharing count : %lld\n", count);
    dr_fprintf(pt->output_file,
                   "=========================================================\n\n\n");


}

void
PrintResult(per_thread_t *pt) 
{
    cache_contention_t cur_cont;
    int cur_thread_id = pt->pt_id;//drcctlib_get_thread_id();
    dr_fprintf(pt->output_file, "current thread id is %d\n ===========================================\n\n", cur_thread_id);
    /*map<int, cache_contention_t>::iterator it = (*thread_count_map).find(cur_thread_id);
    if (it == (*thread_count_map).end()) {
        dr_fprintf(pt->output_file, "current thread doesn't exist! Something goes wrong!!\n");
        return ;
    }
    dr_fprintf(pt->output_file, "total memory reference nums are %d  total cache miss are %d total cache invalidation are %d \n\n",
										total_mem_ref_count, total_miss_count, total_invalidation_count);
    dr_fprintf(pt->output_file, "total cache miss / total mem ref percentage : %2f% \n\n", 
										(double)total_miss_count / (double)total_mem_ref_count * 100);
    dr_fprintf(pt->output_file, "total cache inval / total mem ref percentage : %2f% \n\n",
										(double)total_invalidation_count / (double)total_mem_ref_count * 100);
    
    dr_fprintf(pt->output_file, "total cold miss nums are : %d\n", total_cold_miss_count);
    dr_fprintf(pt->output_file, "======================================\n\n");
    dr_fprintf(pt->output_file, "thread miss count : %d  total miss count : %d  cur_thread miss / total miss percentage : %2f% \n\n", 
                                                                                it->second.miss_count, total_miss_count, 
                                                                                    ((double)it->second.miss_count / total_miss_count) * 100);
    dr_fprintf(pt->output_file, "thread inval count : %d  total inval count : %d cur_thread inval / total inval  percentage : %2f% \n\n", 
                                                                                it->second.inval_count, total_invalidation_count,
                                                                                    ((double)it->second.inval_count / total_invalidation_count) * 100);
    */
   map<uint64_t, int> *temp_map = pt->false_sharing_map;
   vector<pair<uint64_t, int>> vmap;
   for (auto it = (*temp_map).begin(); it != (*temp_map).end(); it++) {
       vmap.push_back(make_pair(it->first, it->second));
       global_false_sharing_map->insert(pair<uint64_t, int>(it->first, it->second));
   }
   //对map中元素按value从大到小排序
   sort(vmap.begin(), vmap.end(),
        [](const pair<uint64_t, int> &x, const pair<uint64_t, int> &y) -> int {
            return x.second > y.second;
        });

   /*map<uint64_t, int>::iterator it = (*temp_map).begin();
   while (it != (*temp_map).end()) {
       print_false_sharing_info(pt, it->first, it->second);
       it++;
   }*/
   map<int, int>::iterator global_it;
   int i = 0;
   for (auto it = vmap.begin(); it != vmap.end(); it++) {
   i++;
       int find_key = (int)(it->first);
       global_it = global_sum_up_false_sharing_map->find(find_key);
       if (global_it != global_sum_up_false_sharing_map->end()) {
           global_it->second += it->second;
       } else {
           int i = it->second;
           global_sum_up_false_sharing_map->insert(pair<int, int>(find_key, i));
       }
       if (i <= 3) {
       print_false_sharing_info(pt, it->first, it->second);
       }
   }
}

static void
ClientThreadEnd(void *drcontext)
{
    per_thread_t *pt = (per_thread_t *)drmgr_get_tls_field(drcontext, tls_idx);
    PrintResult(pt);
    dr_close_file(pt->output_file);
    delete pt->false_sharing_map;
#ifdef DEBUG_CACHE_CONTENTION
    dr_close_file(pt->log_file);
#endif
    dr_thread_free(drcontext, pt, sizeof(per_thread_t));
}

static void
ClientInit(int argc, const char *argv[])
{
    char name[MAXIMUM_FILEPATH] = "";
    DRCCTLIB_INIT_LOG_FILE_NAME(
        name, "drcctlib_false_sharing_detection", "out");
    g_folder_name.assign(name, strlen(name));
    mkdir(g_folder_name.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
}

static inline void
print_global_false_sharing_info(uint64_t ctxt, int count)
{
    uint64_t create_cur_ctxt = ctxt;
    int create_ctxt = (int)ctxt;
    int cur_ctxt = (int)(ctxt >> 32);
    dr_fprintf(global_output_file,
                   "=========================create=========================\n");
    if (create_ctxt > 0) {
        drcctlib_print_backtrace(global_output_file, (context_handle_t)create_ctxt,
                                true, true, MAX_CLIENT_CCT_PRINT_DEPTH);
    } else if (create_ctxt < 0) {
        dr_fprintf(global_output_file, "STATIC_OBJECT %s\n",
                    drcctlib_get_str_from_strpool(-create_ctxt));
    } else {
        dr_fprintf(global_output_file, "STACK_OBJECT/UNKNOWN_OBJECT\n");
    }
    dr_fprintf(global_output_file,
                   "===========================use===========================\n");
    drcctlib_print_backtrace(global_output_file, (context_handle_t)cur_ctxt, true,
                            true, MAX_CLIENT_CCT_PRINT_DEPTH);
    dr_fprintf(global_output_file,
                    "false sharing count : %d\n", count);
    dr_fprintf(global_output_file,
                   "=========================================================\n\n\n");
}

static inline void
print_global_sum_up_false_sharing_info(int ctxt, int count)
{
    int create_ctxt = ctxt;
    dr_fprintf(global_sum_file,
                   "=========================create=========================\n");
    if (create_ctxt > 0) {
        drcctlib_print_backtrace(global_sum_file, (context_handle_t)create_ctxt,
                                true, true, MAX_CLIENT_CCT_PRINT_DEPTH);
    } else if (create_ctxt < 0) {
        dr_fprintf(global_sum_file, "STATIC_OBJECT %s\n",
                    drcctlib_get_str_from_strpool(-create_ctxt));
    } else {
        dr_fprintf(global_sum_file, "STACK_OBJECT/UNKNOWN_OBJECT\n");
    }
    dr_fprintf(global_sum_file,
                    "false sharing count : %d\n", count);
    dr_fprintf(global_sum_file,
                   "=========================================================\n\n\n");
}

static void
print_global_sum_up_result()
{
   vector<pair<int, int>> vmap;
   for (auto it = (*global_sum_up_false_sharing_map).begin(); it != (*global_sum_up_false_sharing_map).end(); it++) {
       vmap.push_back(make_pair(it->first, it->second));
   }
   //��map��Ԫ�ذ�value�Ӵ�С����
   sort(vmap.begin(), vmap.end(),
        [](const pair<int, int> &x, const pair<int, int> &y) -> int {
            return x.second > y.second;
        });

    for (auto it = vmap.begin(); it != vmap.end(); it++) {
       print_global_sum_up_false_sharing_info(it->first, it->second);
   }
}

static void
print_global_result()
{
   vector<pair<uint64_t, int>> vmap;
   for (auto it = (*global_false_sharing_map).begin(); it != (*global_false_sharing_map).end(); it++) {
       vmap.push_back(make_pair(it->first, it->second));
   }
   //��map��Ԫ�ذ�value�Ӵ�С����
   sort(vmap.begin(), vmap.end(),
        [](const pair<uint64_t, int> &x, const pair<uint64_t, int> &y) -> int {
            return x.second > y.second;
        });

    for (auto it = vmap.begin(); it != vmap.end(); it++) {
       print_global_false_sharing_info(it->first, it->second);
   }
}

//2022.11.29 add hpcviewer fmt output format
static void
print_global_result_hpc_fmt()
{
    vector<pair<uint64_t, int>> vmap;
   for (auto it = (*global_false_sharing_map).begin(); it != (*global_false_sharing_map).end(); it++) {
       vmap.push_back(make_pair(it->first, it->second));
   }
   //��map��Ԫ�ذ�value�Ӵ�С����
   sort(vmap.begin(), vmap.end(),
        [](const pair<uint64_t, int> &x, const pair<uint64_t, int> &y) -> int {
            return x.second > y.second;
        });

    vector<HPCRunCCT_t *> hpcRunNodes;
    for (auto it = vmap.begin(); it != vmap.end(); it++) {
       context_handle_t create_hndl = (int) it->first;
       context_handle_t occur_hndl = (int) (it->first >> 32);
       int count = it->second;
       if (create_hndl <= 0) {
        dr_printf("STATIC OBJECT, please refer to txt file!\n");
        continue;
       }
       HPCRunCCT_t *hpcRunNode = new HPCRunCCT_t();
       hpcRunNode->ctxt_hndl_list.push_back(create_hndl);
       hpcRunNode->ctxt_hndl_list.push_back(occur_hndl);
       hpcRunNode->metric_list.push_back(count);
       hpcRunNodes.push_back(hpcRunNode);
   }

   build_progress_custom_cct_hpurun_format(hpcRunNodes);
   write_progress_custom_cct_hpurun_format();
}

static void
ClientExit(void)
{
    /*
    char name[MAXIMUM_FILEPATH] = "";
    sprintf(name + strlen(name), "%s/global-all-threads-info", g_folder_name.c_str());
    global_output_file = dr_open_file(name, DR_FILE_WRITE_OVERWRITE | DR_FILE_ALLOW_LARGE);
    print_global_result();
    char name1[MAXIMUM_FILEPATH] = "";
    sprintf(name1 + strlen(name1), "%s/global-all-threads-sum-up-info", g_folder_name.c_str());
    global_sum_file = dr_open_file(name1, DR_FILE_WRITE_OVERWRITE | DR_FILE_ALLOW_LARGE);
    print_global_sum_up_result();
    */
    print_global_result_hpc_fmt();
    drcctlib_exit();
    hpcrun_format_exit();
    if (!drmgr_unregister_thread_init_event(ClientThreadStart) ||
        !drmgr_unregister_thread_exit_event(ClientThreadEnd) ||
        !drmgr_unregister_tls_field(tls_idx)) {
        DRCCTLIB_PRINTF(
            "ERROR: drcctlib_false_sharing_detection failed to unregister in ClientExit");
    }
    drmgr_exit();
}

#ifdef __cplusplus
extern "C" {
#endif

DR_EXPORT void
dr_client_main(client_id_t id, int argc, const char *argv[])
{
    dr_set_client_name("DynamoRIO Client 'drcctlib_false_sharing_detection'",
                       "http://dynamorio.org/issues");
    ClientInit(argc, argv);

    if (!drmgr_init()) {
        DRCCTLIB_EXIT_PROCESS(
            "ERROR: drcctlib_false_sharing_detection unable to initialize drmgr");
    }
    drmgr_priority_t thread_init_pri = { sizeof(thread_init_pri),
                                         "drcctlib_false_sharing-thread_init", NULL, NULL,
                                         DRCCTLIB_THREAD_EVENT_PRI + 1 };
    drmgr_priority_t thread_exit_pri = { sizeof(thread_exit_pri),
                                         "drcctlib_false_sharing-thread-exit", NULL, NULL,
                                         DRCCTLIB_THREAD_EVENT_PRI + 1 };
    drmgr_register_thread_init_event_ex(ClientThreadStart, &thread_init_pri);
    drmgr_register_thread_exit_event_ex(ClientThreadEnd, &thread_exit_pri);
    tls_idx = drmgr_register_tls_field();
    if (tls_idx == -1) {
        DRCCTLIB_EXIT_PROCESS(
            "ERROR: drcctlib_false_sharing_detection drmgr_register_tls_field fail");
    }
    drcctlib_init_ex(DRCCTLIB_FILTER_MEM_ACCESS_INSTR, INVALID_FILE, NULL, NULL,
                     InstrumentPerBBCache,
                     DRCCTLIB_COLLECT_DATA_CENTRIC_MESSAGE | DRCCTLIB_CACHE_MODE |
                         DRCCTLIB_CACHE_MEMEORY_ACCESS_ADDR);
    hpcrun_format_init(dr_get_application_name(), false);
    hpcrun_create_metric("FALSE_SHARING_COUNT");
    
    dr_register_exit_event(ClientExit);
}

#ifdef __cplusplus
}
#endif
